home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / snz128s / src / history.c < prev    next >
C/C++ Source or Header  |  1994-05-23  |  11KB  |  472 lines

  1. /*
  2.     SNEWS 2.0
  3.  
  4.     History routines
  5.  
  6.  
  7.     Copyright (C) 1991  John McCombs, Christchurch, NEW ZEALAND
  8.                         john@ahuriri.gen.nz
  9.                         PO Box 2708, Christchurch, NEW ZEALAND
  10.  
  11.     This program is free software; you can redistribute it and/or modify
  12.     it under the terms of the GNU General Public License, version 1, as
  13.     published by the Free Software Foundation.
  14.  
  15.     This program is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU General Public License for more details.
  19.  
  20.     See the file COPYING, which contains a copy of the GNU General
  21.     Public License.
  22.  
  23.  
  24.  */
  25.  
  26. /*---------------------------- Source Control ------------------------------*/
  27.  
  28. /*
  29.  * $Id: HISTORY.C,v 1.2 1994/02/05 18:48:42 gbj Exp user $
  30.  */
  31.  
  32. /****************************************************************************
  33. *   06 Jun 92   1.2     GT  Invalidate freed pointers.                      *
  34. *                           Fix NULL pointer dereference.                   *
  35. *   12 Jun 92   1.3    NJL  Restructure add_hist_record() routine.          *
  36. *                           Fix find_msg_id() to resolve hash collisions.   *
  37. *                           Put ALL messages in history file to catch any   *
  38. *                           duplicates whilst processing CIX scratchpads.   *
  39. *                           Actually this belongs in the CIX unbatcher.     *
  40. *                           Ensure strtok() accepts tab as well as space.   *
  41. *   02 Jul 92   1.4     GT  Change history file name.                       *
  42. *   17 Jul 92   1.5     GT  C++ compilation.                                *
  43. *                           Heap debugging.                                 *
  44. *   16 Aug 92   1.6    MSM  Snews 1.9                                       *
  45. *                           Flush writes to history.snw                     *
  46. *   31 May 93   2.7    MSM  Snews 2.0                                       *
  47. *   18 Jun 93    2.8    MSM  Heap debugging removed (using Bounds Check)     *
  48. *   18 Sep 93    2.9    MSM  File handle loss problem fixed                  *
  49. ****************************************************************************/
  50.  
  51. #include "defs.h"
  52. #include "history.h"
  53. #include <io.h>
  54. #include <ctype.h>
  55.  
  56. #ifdef ATARI
  57. #    include "fileops.h"
  58. #    include "screen.h"
  59. #endif
  60.  
  61. #ifndef __TURBOC__
  62. #ifndef ATARI
  63. unsigned long farcoreleft(void);
  64. unsigned long testcoreleft(void);
  65. #endif
  66. #endif
  67.  
  68. static FILE    *hist = NULL;
  69. static HIST_LIST *hlist, *hlistend;
  70.  
  71.  
  72. /*----------------------- open hist file for writing ------------------------*/
  73. FILE           *open_hist_file(void)
  74. {
  75.  
  76.     /*
  77.      * This routine opens the history file for writing, positioned after
  78.      * the last record.
  79.      */
  80.  
  81.     char            fn[256];
  82.  
  83.     sprintf(fn, "%shistory.snw", my_stuff.news_dir);
  84.  
  85.     if (hist != NULL) {
  86.         fclose(hist);
  87.         hist = NULL;
  88.     }
  89.  
  90.     if ((hist = fopen(fn, "a+b")) == NULL) {
  91.         fprintf(stderr, "history: cannot open file %s for append\n", fn);
  92.         exit(1);
  93.     }
  94.  
  95.     return (hist);
  96. }
  97.  
  98.  
  99. /*---------------------------- close hist file ---------------------------*/
  100. void            close_hist_file(void)
  101. {
  102.  
  103.     /*
  104.      * This routine closes the history file
  105.      */
  106.  
  107.      if (hist != NULL) {
  108.         fclose(hist);
  109.         hist = NULL;
  110.      }
  111. }
  112.  
  113.  
  114. /*---------------------------- add record to list ------------------------*/
  115. int             add_to_hist_list(char *p, int ct, long where)
  116. {
  117.  
  118.     /*
  119.      * This routine adds a single entry to the history list in memory. It returns 1 if store is exhausted, else zero.
  120.      */
  121.  
  122.     if (hlist == NULL) {
  123.         hlist = (HIST_LIST *) malloc(sizeof(HIST_LIST));
  124.         hlistend = hlist;
  125.     }
  126.     else {
  127.         hlistend->next = (HIST_LIST *) malloc(sizeof(HIST_LIST));
  128.         hlistend = hlistend->next;
  129.     }
  130.  
  131.     hlistend->mid = hash_msg_id(p);
  132.     hlistend->ngroups = ct;
  133.     hlistend->offset = where;
  134.     hlistend->next = NULL;
  135.  
  136.     /* leave some memory for other things */
  137. #ifdef ATARI
  138.     if (farcoreleft() < HIST_MEM_LIMIT)
  139. #else
  140. #ifdef __TURBOC__
  141.     if (farcoreleft() < HIST_MEM_LIMIT)
  142. #else
  143.     if (testcoreleft() < HIST_MEM_LIMIT)
  144. #endif
  145. #endif
  146.  
  147.         return (1);
  148.     else
  149.         return (0);
  150. }
  151.  
  152. /*---------------------------- add record to file -----------------------*/
  153. void            add_hist_record(char *msg_id, char *ng)
  154. {
  155.  
  156.     /*
  157.      * This routine adds a record to the history files.  It is passed the
  158.      * message id, and the newsgroup list.  The newsgroup list is unwound,
  159.      * and the article numbers are added to the record.
  160.      * 
  161.      * We assume that this routine is called after the article has been
  162.      * posted, so that the article counters are correct.
  163.      */
  164.  
  165.  
  166.     char           *p;
  167.     static char     buf[512];
  168.     ACTIVE         *a;
  169.     time_t          t;
  170.     int             ct;
  171.     int             junk_flag;        /* nz - target is junk group */
  172. #ifndef ATARI
  173.     int             dup_h;
  174. #endif
  175.  
  176.     time(&t);
  177.  
  178.     /* count the newsgroups */
  179.  
  180.     strcpy(buf, ng);
  181.     p = strtok(buf, " \t,\n\r");
  182.     ct = 0;
  183.  
  184.     while (p != NULL) {
  185.         ct++;
  186.         p = strtok(NULL, " \t,\n\r");
  187.     }
  188.  
  189.     strcpy(buf, ng);
  190.     p = strtok(buf, " \t,\n\r");
  191.  
  192.     if ((p != NULL) && (strlen(msg_id) > 0)) {
  193.  
  194.         fseek(hist, 0L, SEEK_END);
  195.  
  196.         add_to_hist_list(msg_id, ct, ftell(hist));
  197.  
  198.         fprintf(hist, "%s %09ld ", msg_id, t);
  199.  
  200.         while (p != NULL) {
  201.             a = find_news_group(p, &junk_flag);
  202.             fprintf(hist, "%s %08ld  ", a->group, a->hi_num);
  203.             p = strtok(NULL, " \t,\n\r");
  204.         }
  205.         fprintf(hist, "\n");
  206. #ifdef ATARI
  207.         fflush(hist);
  208. #else
  209.         dup_h = dup(fileno(hist));
  210.         close(dup_h);
  211. #endif
  212.         if (ferror(hist))
  213.             perror("Error whilst appending to History file");
  214.     }
  215. }
  216.  
  217.  
  218. /*--------------------- read history file into ram -------------------------*/
  219. HIST_LIST      *load_history_list(int all)
  220. {
  221.  
  222.     /*
  223.      * This routine opens and reads the history file, building and index
  224.      * 
  225.      * Load this after active and ng files.  Set HIST_MEM_LIMIT in defs.h
  226.      * to ensure there is enough memory left
  227.      */
  228.  
  229.     static char     buf[512];
  230.     char           *msg_id, *p;
  231.     int             ct, ans = 0;
  232.     long            where;
  233.  
  234.     sprintf(buf, "%shistory.snw", my_stuff.news_dir);
  235.  
  236.     if (hist != NULL)
  237.     {
  238.         fclose(hist);
  239.         hist = NULL;
  240.     }
  241.  
  242.     if ((hist = fopen(buf, "rb")) == NULL) {
  243.         fprintf(stderr, "history: cannot find history file <%s>\n", buf);
  244.         fprintf(stderr, "         make new one ? (Y/N) ");
  245.         ans = getch();
  246.         ans = tolower(ans);
  247.         putch(ans);
  248.         if (ans == (int) 'y') {
  249.             if ((hist = fopen(buf, "w+b")) == NULL) {
  250.                 fprintf(stderr, "history: cannot make history file <%s>\n", buf);
  251.                 exit(1);
  252.             }
  253.         }
  254.         else
  255.             exit(1);
  256.     }
  257.  
  258.     where = 0;
  259.     hlist = hlistend = NULL;
  260.  
  261.     while (fgets(buf, 511, hist)) {
  262.  
  263.         msg_id = strtok(buf, " \t\n\r");
  264.  
  265.         /* skip the next entry */
  266.         p = strtok(NULL, " \t\n\r");
  267.  
  268.         /* now count the newsgroups */
  269.         ct = 0;
  270.  
  271.         p = strtok(NULL, " \t\n\r");
  272.         while (p != NULL) {
  273.             ct++;
  274.             p = strtok(NULL, " \t,\n\r");
  275.             p = strtok(NULL, " \t,\n\r");
  276.         }
  277.  
  278.         if ((all == 0) || (ct > 1))
  279.             ans = add_to_hist_list(msg_id, ct, where);
  280.  
  281.         if (ans)
  282.             break;
  283.  
  284.         where = ftell(hist);
  285.     }
  286.  
  287.     return (hlist);
  288. }
  289.  
  290. /*------------------------- release the history list ----------------------*/
  291. void            free_hist_list(void)
  292. {
  293.  
  294.     /*
  295.      * Close the history file and free the memory list
  296.      */
  297.  
  298.     HIST_LIST      *h;
  299.  
  300.     close_hist_file();
  301.  
  302.     while (hlist != NULL) {
  303.         h = hlist->next;
  304.         free(hlist);
  305.         hlist = h;
  306.     }
  307. }
  308.  
  309. /*-------------------------- find history entry -----------------------------*/
  310. HIST_LIST      *find_msg_id(char *msg_id)
  311. {
  312.  
  313.     /*
  314.      * Look up the history list and return the entry for the req'd msg id
  315.      * or NULL if not found. ASSUMES global file 'hist' is open. If it
  316.      * isn't this routine always returns NULL.
  317.      */
  318.  
  319.     static char     buf[512];              /* THIS MUST BE STATIC !!! */
  320.     HIST_LIST      *h;
  321.     char           *p;
  322.     long            hashed_id;
  323.  
  324.     hashed_id = hash_msg_id(msg_id);
  325.  
  326.     for (h = hlist; h != NULL; h = h->next) {
  327.         if (h->mid == hashed_id) {
  328.             /* Look up this entry in the file */
  329.             fseek(hist, h->offset, SEEK_SET);
  330.             if (fgets(buf, 511, hist)) {
  331.  
  332.                 /*
  333.                  * Compare the target msg id with the one on the file.
  334.                  * If they are different then there must be a
  335.                  * hash collision so we must continue searching.
  336.                  */
  337.  
  338.                 p = strtok(buf, " \t\n\r");
  339.                 if (stricmp(p, msg_id) == 0)
  340.                     return (h);
  341.             }
  342.         }
  343.     }
  344.  
  345.     return (NULL);
  346. }
  347.  
  348. /*----------------------- lookup cross posts --------------------------------*/
  349. CROSS_POSTS    *look_up_history(char *msg_id, char *ng)
  350. {
  351.  
  352.     /*
  353.      * This routine returns a linked list of the groups to which
  354.      * an article has been crossposted.  Self is excluded. To do
  355.      * this we:
  356.      *    - hash the msg id
  357.      *    - search the list of id's
  358.      *    - if a hit is found we read it from the hist file
  359.      *    - decode the record, excluding self
  360.      *    - build a CROSS_POSTS list
  361.      * 
  362.      * NULL is returned if there were no crossposts.
  363.      */
  364.  
  365.     HIST_LIST      *h;
  366.     CROSS_POSTS    *cx, *c;
  367.     char           *p;
  368.  
  369.     h = find_msg_id(msg_id);
  370. #ifdef ATARI
  371.     if (h == NULL)
  372.         return NULL;
  373.     if (h->ngroups < 2)
  374.         return NULL;
  375. #else
  376.     if (h == NULL || h->ngroups < 2)
  377.         return (NULL);                      /* GT 06 Jun 92 */
  378. #endif
  379.     cx = NULL;
  380.     c = NULL;
  381.  
  382.     /*
  383.      * skip the date field and leave pointing to the first group.
  384.      * Wot a bodge. Strtok is now returning fields from the
  385.      * buffer in find_msg_id. Good job it's static ;-)
  386.      */
  387.  
  388.     p = strtok(NULL, " \t\n\r");          /* get date */
  389.     p = strtok(NULL, " \t\n\r");          /* get first newsgroup */
  390.  
  391.     while (p != NULL) {
  392.  
  393.         /* exclude self */
  394.         if (stricmp(p, ng) != 0) {
  395.             if (cx == NULL) {
  396.                 cx = (CROSS_POSTS *) malloc(sizeof(CROSS_POSTS));
  397.                 c = cx;
  398.             }
  399.             else {
  400.                 c->next = (CROSS_POSTS *) malloc(sizeof(CROSS_POSTS));
  401.                 c = c->next;
  402.             }
  403.  
  404.             strcpy(c->group, p);
  405.             p = strtok(NULL, " \t\n\r");  /* get local article number */
  406.             c->art_num = atol(p);
  407.         }                                  /* if (stricmp(p, ng) != 0) */
  408.         else {
  409.             /* eat article number */
  410.             p = strtok(NULL, " \t\n\r");
  411.         }
  412.  
  413.         if (c != NULL)                      /* GT 06 Jun 92 */
  414.             c->next = NULL;
  415.  
  416.         p = strtok(NULL, " \t\n\r");      /* get next newsgroup */
  417.     }                                      /* while (p != NULL) */
  418.  
  419.     return (cx);
  420.  
  421. }
  422.  
  423. /*----------------------- deallocate crosspost list --------------------------*/
  424. void            free_cross_post_list(CROSS_POSTS * cx)
  425. {
  426.     CROSS_POSTS    *c;
  427.  
  428.     while (cx != NULL) {
  429.         c = cx->next;
  430.         free(cx);
  431.         cx = c;
  432.     }
  433.  
  434. }
  435.  
  436. /*------------------------ random number generator -------------------------*/
  437. long            seed;
  438.  
  439. long            xrand(void)
  440. {
  441.     seed = (seed * 16807) & 0x7FFFFFFFL;
  442.     return (seed);
  443. }
  444.  
  445. /*----------------------------- hash a key -----------------------------------*/
  446. long            hash_key(long s, char *key)
  447. {
  448.     long            hash_num;
  449.     int             i;
  450.  
  451.     hash_num = 0;
  452.     seed = s;
  453.  
  454.     for (i = 0; i < (int)strlen(key); i++) {
  455.         hash_num += xrand() * (key[i] + 0xFF00);
  456.     }
  457.  
  458.     hash_num = hash_num & 0x7FFFFFFFL;
  459.     if (hash_num == 0)
  460.         hash_num++;
  461.  
  462.     return (hash_num);
  463. }
  464.  
  465.  
  466. /*----------------------------- hash the message id -------------------------*/
  467. long            hash_msg_id(char *msg_id)
  468. {
  469.  
  470.     return (hash_key(hash_key(26l, msg_id), msg_id));
  471. }
  472.